(function (window) {

    /**
     * 定义 MyPromise 构造函数
     * @param {Function} executor 执行器函数
     */
    function MyPromise(executor) {

        const self = this
        this.status = 'pending'     // promise 的状态
        this.data = undefined       // promise 的结果数据
        this.callbacks = []         // 存储回调函数，每个元素结构：{ onResolved() {}, onRejected() {} }

        function resolve(value) {
            if (self.status !== 'pending') return
            self.status = 'resolved'
            self.data = value
            if (self.callbacks.length) {    // 如果有待执行的 callback 函数，就异步执行回调函数 onResolved
                setTimeout(() => {          // 放入队列中执行所有成功的回调
                    self.callbacks.forEach(callbackObj => {
                        callbackObj.onResolved(value)
                    })
                })
            }
        }

        function reject(reason) {
            if (self.status !== 'pending') return
            self.status = 'rejected'
            self.data = reason
            if (self.callbacks.length) {    // 如果有待执行的 callback 函数，就异步执行回调函数 onRejected
                setTimeout(() => {          // 放入队列中执行所有失败的回调
                    self.callbacks.forEach(callbackObj => {
                        callbackObj.onRejected(reason)
                    })
                })
            }
        }

        try {
            executor(resolve, reject)
        } catch (err) {      // 如果执行器抛出异常，promise 对象就变成 rejected 状态
            reject(err)
        }

    }

    /**
     * 在原型对象上绑定 then 方法
     * @param {Function} onResolved 成功的回调
     * @param {Function} onRejected 失败的回调
     * @returns promise 对象
     */
    MyPromise.prototype.then = function (onResolved, onRejected) {
        const self = this

        onResolved = typeof onResolved === 'function' ? onResolved : value => value                 // 向后传递成功的 value
        // 指定默认的失败的回调（实现错误/异常传透的关键）
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }     // 向后传递失败的 reason

        return new MyPromise((resolve, reject) => {
            function handle(callback) {
                try {
                    const result = callback(self.data)
                    if (result instanceof MyPromise) {      // 3. 如果回调返回 promise，return 的 promise 结果就是回调返回的 promise 结果
                        // result.then(value => resolve(value), reason => reject(reason))
                        result.then(resolve, reject)        // 上一行代码简写形式
                    } else {                                // 2. 如果回调返回的不是 promise，return 的 promise 就会成功，value 是回调返回的值
                        resolve(result)
                    }
                } catch (error) {
                    reject(error)                           // 1. 如果回调抛出异常，return 失败的 promise，reason 就是 error
                }
            }
            if (self.status === 'pending') {                    // 当前状态是 pending，保存回调函数
                self.callbacks.push({
                    onResolved() {
                        handle(onResolved)
                    },
                    onRejected() {
                        handle(onRejected)
                    }
                })
            } else if (self.status === 'resolved') {
                setTimeout(() => {
                    handle(onResolved)
                })
            } else if (self.status === 'rejected') {
                setTimeout(() => {
                    handle(onRejected)
                })
            }
        })
    }

    /**
     * 在原型对象上绑定 catch 方法
     * @param {Function} onRejected 失败的回调
     * @returns promise 对象
     */
    MyPromise.prototype.catch = function (onRejected) {
        return this.then(null, onRejected)
    }

    /**
     * 在函数对象上绑定 resolve 方法
     * @param {any} value 成功时的值
     * @returns 解析 value 后的 promise 对象
     */
    MyPromise.resolve = function (value) {
        return new MyPromise((resolve, reject) => {
            if (value instanceof MyPromise) {
                value.then(resolve, reject)
            } else {
                resolve(value)
            }
        })
    }

    /**
     * 在函数对象上绑定 reject 方法
     * @param {any} reason 失败时的值
     * @returns 解析 reason 后的 promise 对象
     */
    MyPromise.reject = function (reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason)
        })
    }

    /**
     * 在函数对象上绑定 all 方法
     * @param {Array} promises 含有 promise 对象的数组
     * @returns 当所有 promise 都成功时，返回成功的 promise；只要有一个 promise 失败，返回失败的 promise
     */
    MyPromise.all = function (promises) {
        const valueArr = new Array(promises.length)
        let resolvedCount = 0
        return new MyPromise((resolve, reject) => {
            promises.forEach((p, index) => {
                MyPromise.resolve(p).then(value => {      // 如果 p 不是 promise，就用 resolve 方法把它变成 promise
                    resolvedCount += 1
                    valueArr[index] = value
                    if (resolvedCount === promises.length) {
                        resolve(valueArr)
                    }
                }, reason => {
                    reject(reason)
                })
            })
        })
    }

    /**
     * 在函数对象上绑定 race 方法
     * @param {Array} promises 含有 promise 对象的数组
     * @returns promise，其结果由第一个返回结果的 promise 的状态决定
     */
    MyPromise.race = function (promises) {
        return new MyPromise((resolve, reject) => {
            promises.forEach((p, index) => {
                MyPromise.resolve(p).then(value => {        // 如果 p 不是 promise，就用 resolve 方法把它变成 promise
                    resolve(value)
                }, reason => {
                    reject(reason)
                })
            })
        })
    }

    /**
     * 在函数对象上绑定 resolveDelay 方法
     * @param {any} value 成功时的值
     * @param {*} delay 延迟时间，单位：ms
     * @returns 解析 value 后的 promise 对象
     */
    MyPromise.resolveDelay = function (value, delay) {
        return new MyPromise((resolve, reject) => {
            setTimeout(() => {
                if (value instanceof MyPromise) {
                    value.then(resolve, reject)
                } else {
                    resolve(value)
                }
            }, delay)
        })
    }

    /**
     * 在函数对象上绑定 rejectDelay 方法
     * @param {any} reason 失败时的值
     * @param {*} delay 延迟时间，单位：ms
     * @returns 解析 reason 后的 promise 对象
     */
    MyPromise.rejectDelay = function (reason, delay) {
        return new MyPromise((resolve, reject) => {
            setTimeout(() => {
                reject(reason)
            }, delay)
        })
    }

    window.MyPromise = MyPromise
})(window)